#已测试的Python版本,3.8.8
import pandas as pd
import numpy as np
#pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
#pip uninstall numpy #若版本太低,需要卸载,重新安装
#已测试版本,1.23.4
import os
import cv2 
#pip install opencv-python
#已测试版本,4.6.0
#如果失败,可尝试下载wheel安装, 以下是镜像地址
#https://pypi.tuna.tsinghua.edu.cn/simple/opencv-contrib-python/
print(cv2.__version__)

#opencv要求路径中不能有中文字符
# 获得图片路径
source_path = r"D:\shapes\triangles\drawing(8).png"

# img表示输入的图片,即为需要进行形状判断的图片
frame = cv2.imread(source_path)  
#读取图像信息
h, w, ch = frame.shape
#关于frame.shape[0],[1],[2]
# frame.shape[0]:图像的垂直尺寸(高度)
# frame.shape[1]:图像的水平尺寸(宽度)
# frame.shape[2]:图像的通道数.

#numpy.zeros:Return a new array of given shape and type, filled with zeros. 
result = np.zeros((h, w, ch), dtype=np.uint8)
# 二值化图像
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 
# 根据阈值处理, #阈值的作用是根据设定的值处理图像的灰度值,比如灰度大于某个数值像素点保留.通过阈值以及有关算法可以实现从图像中抓取特定的图形,比如去除背景等
# The function cv.threshold is used to apply the thresholding. 
# The first argument is the source image, which should be a grayscale image. 
# The second argument is the threshold value which is used to classify the pixel values. 
# The third argument is the maximum value which is assigned to pixel values exceeding the threshold. 
# OpenCV provides different types of thresholding which is given by the fourth parameter of the function. Basic thresholding as described above is done by using the type cv.THRESH_BINARY. 
# use Otsu algorithm to choose the optimal threshold value
ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)  

#cv2.imshow("input image", frame)

# 寻找轮廓的图像,cv2.findContours()函数
# mode-轮廓的检索模式:
#     cv2.RETR_EXTERNAL表示只检测外轮廓
#     cv2.RETR_LIST检测的轮廓不建立等级关系
#     cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息.如果内孔内还有一个连通物体,这个物体的边界也在顶层.
#     cv2.RETR_TREE建立一个等级树结构的轮廓.
#
# method-为轮廓的近似办法:
#     cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
#     cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标
#
# 返回两个值,一个是轮廓本身(countours),还有一个是每条轮廓对应的属性(hierarchy).Each individual contour is a Numpy array of (x,y) coordinates of boundary points of the object.
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 提取与绘制轮廓
for i in range(len(contours)):
    cv2.drawContours(result, contours, i, (0, 255, 0), 2)
    # 第一个参数是指明在哪幅图像上绘制轮廓;image为三通道才能显示轮廓
    # 第二个参数是轮廓本身,在Python中是一个list;
    # 第三个参数指定绘制轮廓list中的哪条轮廓,如果是 - 1,则绘制其中的所有轮廓
    # 第四个参数为contour_color represents the contours color.
    # 第五个参数为contour_thickness represents the thickness of the lines forming the contours.

    # 计算轮廓的周长,函数cv2.arcLenngth()
    epsilon = 0.05 * cv2.arcLength(contours[i], True)
    # 计算得到.这个函数的第二参数可以用来指定对象的形状是闭合(True),还是打开的(一条曲线).

    # cv2.approxPolyDP(), that allows us to approximate contours in an image. Approximating contours allows us to straighten out various contours to make them curvy or messy. Lines that aren't straight may be straightened. 

    approx = cv2.approxPolyDP(contours[i], epsilon, True) # 将轮廓形状近似到另外一种由更少点组成的轮廓形状,新轮廓的点的数目有我们设定的准确度来决定.假设我们要在一幅图像中查找一个矩形,但是由于图像的种种原因,我们不能得到一个完美的矩形,而是一个不规则形状,现在就可以使用这个函数来近似这个形状了.这个函数的第二个参数叫epsilon,它是从原始轮廓到近似轮廓的最大距离,它是一个准确率参数,选择一个好的epsilon对于得到满意结果非常重要.

    #print(approx)
    # 分析几何形状
    corners = len(approx)
    print(corners)
    shape_type = ""
    if corners == 3:
        shape_type = "三角形"
        print(shape_type)
    if corners == 4:
        shape_type = "矩形"
        print(shape_type)
    if corners > 4:
        shape_type = "圆形"
        print(shape_type)
#